package com.onpositive.mediawiki.dataprocessors;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map.Entry;

import com.onpositive.mediawiki.propertyparser.PropertyCharacteristic;
import com.onpositive.units.ParsedProperty;
import com.onpositive.units.ParsedValue;
import com.onpositive.utils.StringLevenshteinDisstanceComputer;

public class DisambiguationProcessor extends AbstractDataProcessor {
	
	private final static int accuracy = 3 ;
	
	public DisambiguationProcessor() {
		super();
	}
	
	public DisambiguationProcessor( String propertyName ) {
		super( propertyName );
	}
	
	public DisambiguationProcessor( String[] propertyNames ) {
		super( propertyNames );
	}
	
	public DisambiguationProcessor( int flags ) {
		super( flags );
	}
	
	public DisambiguationProcessor(String propertyName, int flags ) {
		super( propertyName, flags );
	}
	
	public DisambiguationProcessor(String propertyNames[], int flags ) {
		super( propertyNames, flags );
	}

	private HashMap<String,Integer> totalMap ;
	private HashMap<String,HashMap<String,Integer>> propertyMaps ;
	
	private HashMap<String,String> totalRulesMap ;
	private HashMap<String,HashMap<String,String>> propertyRulesMaps ;

	@Override
	protected void readFlags(int flags) {
	}

	@Override
	protected boolean checkApplicability(PropertyCharacteristic pc){

		return pc.isAcceptsDisambiguation() ;
	}

	@Override
	protected void doGatherPropertyStats(ParsedProperty prop) {
		
		String propertyName = prop.getName() ;
		Iterable<ParsedValue> values = prop.getValues() ;
		
		if( propertyMaps == null )
			propertyMaps = new HashMap<String, HashMap<String,Integer>>() ;
		
		HashMap<String,Integer> pMap = propertyMaps.get( propertyName );
		if( pMap == null )
		{
			pMap = new HashMap<String, Integer>() ;
			propertyMaps.put( propertyName, pMap ) ;
		}
		
		for( ParsedValue pVal : values )
		{
			String str = (String)pVal.value() ;
			Integer count = pMap.get( str ) ;
			count = ( count != null ) ? count + 1 : 1 ;
			pMap.put( str, count ) ;
		}		
	}

	@Override
	protected void doGatherTotalStats(ParsedProperty prop) {
		
		Iterable<ParsedValue> values = prop.getValues() ;
		
		if( totalMap == null )
			totalMap = new HashMap<String, Integer>() ;
		
		for( ParsedValue pVal : values )
		{
			String str = (String)pVal.value() ;
			Integer count = totalMap.get( str ) ;
			count = ( count != null ) ? count + 1 : 1 ;
			totalMap.put( str, count ) ;
		}		
	}

	@Override
	protected void doApplyPropertyChange(ParsedProperty prop) {
		
		if( this.propertyRulesMaps == null )
			processStats() ;
		
		if( this.propertyRulesMaps == null )
			return ;
		
		HashMap<String, String> pMap = this.propertyRulesMaps.get( prop.getName() ) ;
		
		if( pMap == null )
			return ;
		
		for( ParsedValue pVal : prop.getValues() )
		{
			String str = (String)pVal.value() ;
			String replacementValue = pMap.get( str ) ;
			if( replacementValue != null )
				pVal.setValue( replacementValue ) ;
		}	
	}

	@Override
	protected void doApplayTotalChange(ParsedProperty prop) {
		
		if( this.totalRulesMap == null )
			processStats() ;
		
		if( this.totalRulesMap == null )
			return ;
		
		for( ParsedValue pVal : prop.getValues() )
		{
			String str = (String)pVal.value() ;
			String replacementValue = totalRulesMap.get( str ) ;
			if( replacementValue != null )
				pVal.setValue( replacementValue ) ;
		}
		
	}

	private void processStats() {
		
		if( this.totalMap != null )
		{
			//use descending order
			Comparator<String> customComp = new Comparator<String>(){
				@Override
				public int compare(String o1, String o2) {
					return totalMap.get(o2) - totalMap.get(o1) ;
				}
			}; 
			
			ArrayList<String> lst = new ArrayList<String>( this.totalMap.keySet() ) ;
			Collections.sort( lst, customComp ) ;
			
			HashMap<String,String> mapTmp = new HashMap<String, String>() ;
			
			for( int i = lst.size()-1 ; i >=0 ; i-- )
			{
				String str1 = lst.get(i) ;
				for( int j = 0 ; j < i ; j++ )
				{
					String str2 = lst.get(j) ;
					int dst = StringLevenshteinDisstanceComputer.computeDisstance(str1, str2) ;
					if( dst < accuracy )
						mapTmp.put( str1, str2 ) ;
				}
			}
			
			if( mapTmp.size() != 0 ){
				this.totalRulesMap = new HashMap<String, String>() ;
			
				for( String str : mapTmp.keySet() )
				{
					String s1 = str ;
					String s2 = str ;
					
					for( ; s1 != null ;  )
					{
						s2 = s1 ;
						s1 = mapTmp.get( s2 ) ;
					}
					this.totalRulesMap.put( str, s2 ) ;				
				}
			}
		}
		
		if( this.propertyMaps != null )
		{
			for( Entry<String, HashMap<String, Integer>> entry : this.propertyMaps.entrySet() )
			{
				String propertyName = entry.getKey() ;
				final HashMap<String, Integer> pMap = entry.getValue() ;
				
				if( propertyName == null || pMap == null )
					continue ;
				
				//use descending order
				Comparator<String> customComp = new Comparator<String>(){
					@Override
					public int compare(String o1, String o2) {
						return pMap.get(o2) - pMap.get(o1) ;
					}
				}; 
				
				ArrayList<String> lst = new ArrayList<String>( pMap.keySet() ) ;
				Collections.sort( lst, customComp ) ;
				
				HashMap<String,String> mapTmp = new HashMap<String, String>() ;
				
				for( int i = lst.size()-1 ; i >=0 ; i-- )
				{
					String str1 = lst.get(i) ;
					for( int j = 0 ; j < i ; j++ )
					{
						String str2 = lst.get(j) ;
						int dst = StringLevenshteinDisstanceComputer.computeDisstance(str1, str2) ;
						if( dst < accuracy )
							mapTmp.put( str1, str2 ) ;
					}
				}
				
				if( mapTmp.size() != 0 ){
					if( this.propertyRulesMaps == null )
						this.propertyRulesMaps = new HashMap<String, HashMap<String,String>>() ;
					
					HashMap<String,String> pRulesMap = this.propertyRulesMaps.get( propertyName ) ;
					if( pRulesMap == null )
					{
						pRulesMap = new HashMap<String, String>() ;
						this.propertyRulesMaps.put( propertyName, pRulesMap ) ;
					}
					
					for( String str : mapTmp.keySet() )
					{
						String s1 = str ;
						String s2 = str ;
						
						for( ; s1 != null ;  )
						{
							s2 = s1 ;
							s1 = mapTmp.get( s2 ) ;
						}
						pRulesMap.put( str, s2 ) ;				
					}
				}								
			}
		}		
	}

	@Override
	protected void doApplyChanges( PropertyCharacteristic pc ) {}


}
